﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MediaBot.Library;
using log4net;
using System.Web;
using System.Net;
using System.Collections;
using System.Xml;
using System.Text.RegularExpressions;

namespace RakutenCrawler
{
    public class FormData
    {
        public Dictionary<string, string> KeyValuePairs { get; set; }
        public string DestinationUrl { get; set; }
        public string StringData { get; set; }

        public FormData()
        {
            KeyValuePairs = new Dictionary<string, string>();
            DestinationUrl = string.Empty;
        }

        public override string ToString()
        {
            var builder = new StringBuilder();
            foreach (var key in KeyValuePairs.Keys)
            {
                builder.AppendFormat("&{0}={1}", key, KeyValuePairs[key]);
            }

            builder.Remove(0, 1);

            return builder.ToString();
        }
    }

    public class RakutenBot
    {
        private ILog _log = LogManager.GetLogger("RakutenBot");
        private CookieContainer _requestCookie;
        
        public const string LoginPageUrl = "https://basket.step.rakuten.co.jp/rms/mall/bs/orderfrom/";
        public const string LoginPostUrl = "https://grp01.id.rakuten.co.jp/rms/nid/vc";
        public const string LogoutUrl = "https://member.id.rakuten.co.jp/rms/nid/logout";

        public FormData SubmitOrderOptionForm(FormData formData, XmlWebPage webPage)
        {
            webPage.PostData = formData.ToString();
            webPage.Download();

            var formNodes = webPage.SelectNodes("//form[contains(@name, 'orderForm')]");
            if (formNodes.Count > 0)
            {
                var formNode = formNodes[0];
                return ExtractFormData(formNode, webPage.XmlNamespaceManager);
            }

            return null;
        }

        public FormData SubmitCartConfirmForm(FormData formData, XmlWebPage webPage)
        {
            formData.KeyValuePairs.Add("submit.x", "126");
            formData.KeyValuePairs.Add("submit.y", "17");

            webPage.PostData = formData.ToString();
            webPage.Download();

            var formNodes = webPage.SelectNodes("//form[contains(@name='orderForm')]");
            if (formNodes.Count > 0)
            {
                var formNode = formNodes[0];
                return ExtractFormData(formNode, webPage.XmlNamespaceManager);
            }

            return null;
        }

        public FormData FinalPurchaseSubmit(FormData formData, XmlWebPage webPage)
        {
            formData.KeyValuePairs.Add("commit.x", "156");
            formData.KeyValuePairs.Add("commit.y", "6");

            webPage.PostData = formData.ToString();
            webPage.Download();

            var regex = new Regex(@"\d{6,}-\d{8,}-\d{10,}");
            var input = webPage.GetData();
            var match = regex.Match(input);
            if (match.Success)
            {
                return new FormData { StringData = match.Value };
            }

            return null;
        }

        public FormData ExtractFormData(XmlNode formNode, XmlNamespaceManager xmlns)
        {
            var formData = new FormData();
            formData.DestinationUrl = formNode.SelectSingleNode("./@action", xmlns).Value;

            var inputNodes = formNode.SelectNodes(".//input", xmlns);
            foreach (XmlNode inputNode in inputNodes)
            {
                var key = inputNode.SelectSingleNode("./@name").Value;
                var value = inputNode.SelectSingleNode("./@value").Value;

                formData.KeyValuePairs.Add(key, value);
            }

            var selectNodes = formNode.SelectNodes(".//select", xmlns);
            foreach (XmlNode selectNode in selectNodes)
            {
                var options = selectNode.SelectNodes(".//option");
                foreach (XmlNode option in options)
                {
                    if (option.Attributes["selected"] != null)
                    {
                        formData.KeyValuePairs.Add(selectNode.SelectSingleNode("./@name", xmlns).Value, option.SelectSingleNode("./@value", xmlns).Value);
                        break;
                    }
                }
            }

            var textAreaNodes = formNode.SelectNodes(".//textarea", xmlns);
            foreach (XmlNode textAreaNode in textAreaNodes)
            {
                var key = textAreaNode.SelectSingleNode("./@name").Value;
                var value = textAreaNode.InnerXml;
                formData.KeyValuePairs.Add(key, value);
            }

            return formData;
        }

        public void Purchase()
        {

        }


        private void ExecutePurchaseProcess(FormData initalData, List<Func<FormData, XmlWebPage, FormData>> processes)
        {
            var namespaces = new ArrayList();
            FormData data = initalData;
            XmlWebPage page = new XmlWebPage(new Uri(data.DestinationUrl), namespaces);
            page.RequestCookieContainer = _requestCookie;

            int i = 0;
            do
            {
                var process = processes[i];
                data = process(data, page);
                var cookie = page.RequestCookieContainer;
                page = new XmlWebPage(new Uri(data.DestinationUrl), namespaces);
                page.RequestCookieContainer = cookie;

                i++;
            } while (i < processes.Count || data != null);
        }


        private string GenerateFormData(Dictionary<string, string> dict)
        {
            var builder = new StringBuilder();
            foreach (var key in dict.Keys)
            {
                builder.AppendFormat("&{0}={1}", key, dict[key]);
            }

            builder.Remove(0, 1);

            return builder.ToString();
        }

        public void Login(string username, string password)
        {
            var loginData = new LoginData { Username = username, Password = password };
            _log.Info("Login with Username: " + username);

            var webpage = new WebPage(new Uri(LoginPostUrl));
            webpage.PostData = "__event=login&service_id=top&u=dhlong%40gmail.com&p=3Pxilon3";

            webpage.Download();
            if (webpage.RequestCookieContainer != null)
                _requestCookie = webpage.RequestCookieContainer;        
        }

        public void Logout()
        {
            var webPage = new WebPage(new Uri(LogoutUrl));
            webPage.RequestCookieContainer = _requestCookie;
            webPage.Download();
        }

        private bool ExtractDataFromLoginPage(LoginData loginData)
        {
            try
            {
                var xmlpage = new XmlWebPage(new Uri(LoginPageUrl), new System.Collections.ArrayList());
                xmlpage.Download();

                var tokenSeedNodes = xmlpage.SelectNodes("//input[@name='tokenSeed']/@value");
                if (tokenSeedNodes.Count > 0)
                {
                    loginData.TokenSeed = tokenSeedNodes[0].InnerXml;
                }

                var eventNodes = xmlpage.SelectNodes("//input[@name='__event']/@value");
                if (eventNodes.Count > 0)
                {
                    loginData.Event = eventNodes[0].InnerXml;
                }

                var returnUrlNodes = xmlpage.SelectNodes("//input[@name='return_url']");
                if (returnUrlNodes.Count > 0)
                {
                    var nsManager = xmlpage.XmlNamespaceManager;
                    var returnUrl = loginData.SbId == -1 ? returnUrlNodes[0].SelectSingleNode("./@data-off", nsManager).Value : returnUrlNodes[0].SelectSingleNode("./@data-on", nsManager).Value;
                    loginData.ReturnUrl = returnUrl;
                }

                var serviceIdNodes = xmlpage.SelectNodes("//input[@name='service_id']/@value");
                if (serviceIdNodes.Count > 0)
                {
                    loginData.ServiceId = serviceIdNodes[0].Value;
                }
            }
            catch (Exception e)
            {
                _log.Error("Login Exception", e);
                return false;
            }

            return true;
        }
    }

    public class LoginData
    {
        public string TokenSeed;
        public string Event;
        public string ServiceId;


        private string _username;
        private string _password;
        private string _returnUrl;

        public string ReturnUrl
        {
            get
            {
                if (!string.IsNullOrEmpty(_returnUrl))
                {
                    _returnUrl = HttpUtility.UrlEncode(_returnUrl);
                }

                return _returnUrl;
            }
            set { _returnUrl = value; }
        }

        public string Username
        {
            get
            {
                if (!string.IsNullOrEmpty(_username))
                {
                    _username = HttpUtility.UrlEncode(_username);
                }

                return _username;
            }
            set { _username = value; }
        }

        public string Password
        {
            get
            {
                if (!string.IsNullOrEmpty(_password))
                {
                    _password = HttpUtility.UrlEncode(_password);
                }

                return _password;
            }
            set { _password = value; }
        }


        public string LoginSubmit
        {
            get { return "%A1%A1%A1%A1%BC%A1%A4%D8%A1%A1%A1%A1"; }
        }

        public int SbId
        {
            get { return -1; }
        }

        public int LoginSubmitX { get { return 116; } }
        public int LoginSubmitY { get { return 27; } }

        public string GetFormData()
        {
            return string.Format("tokenSeed={0}&u={1}&p={2}&__event={3}&service_id={4}&return_url={5}&login_submit.x={6}&login_submit.y={7}&login_submit={8}", 
                TokenSeed, Username, Password, Event, ServiceId, ReturnUrl, LoginSubmitX, LoginSubmitY, LoginSubmit);
        }
    }
}
